﻿using System;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace wojilu
{
    /// <summary>
    /// IP相关
    /// </summary>
    public class IPHelp
    {
        #region 获取IP及网卡MAC
        /// <summary>
        /// 取得客户端真实IP。如果有代理则取第一个非内网地址
        /// </summary>
        public static string GetIPAddress()
        {
            string result = String.Empty;
            if (UrlOper.GetServerString("HTTP_X_ForWARDED_For").StrIsNullOrEmpty())
                result = UrlOper.GetServerString("REMOTE_ADDR");
            else
                result = UrlOper.GetServerString("HTTP_X_ForWARDED_For");

            if (result.StrIsNullOrEmpty())
                result = System.Web.HttpContext.Current.Request.UserHostAddress;

            if (result.StrIsNullOrEmpty())
            {
                //可能有代理 
                if (result.IndexOf(".") == -1)    //没有“.”肯定是非IPv4格式 
                    result = null;
                else
                {
                    if (result.IndexOf(",") != -1)
                    {
                        //有“,”，估计多个代理。取第一个不是内网的IP。 
                        result = result.Replace(" ", "").Replace("'", "");
                        string[] temparyip = result.Split(",;".ToCharArray());
                        for (int i = 0; i < temparyip.Length; i++)
                        {
                            if (PageValidator.IsIPAddress(temparyip[i])
                                && temparyip[i].Substring(0, 3) != "10."
                                && temparyip[i].Substring(0, 7) != "192.168"
                                && temparyip[i].Substring(0, 7) != "172.16.")
                            {
                                return temparyip[i];    //找到不是内网的地址 
                            }
                        }
                    }
                    else if (PageValidator.IsIPAddress(result)) //代理即是IP格式 
                        return result;
                    else
                        return "1.1.1.1";    //代理中的内容 非IP，取IP 
                }
            }
            return result;
        }

        /// <summary>
        /// 格式化IP
        /// </summary>
        /// <param name="ipStr">IP地址</param>
        /// <returns></returns>
        public static string FormatIp(string ipStr)
        {
            string[] strArray = ipStr.Split('.');
            string str = "";
            int i;
            for (i = 0; i < strArray.Length; i++)
            {
                if (strArray[i].Length < 3)
                {
                    strArray[i] = Convert.ToString("000" + strArray[i]).Substring(Convert.ToString("000" + strArray[i]).Length - 3, 3);
                }
                str = str + strArray[i].ToString();
            }
            return str;
        }

        /// <summary>
        /// 获取主机IP地址
        /// </summary>
        /// <returns></returns>
        public static IPAddress GetHostIP()
        {
            return GetHostIP(string.Empty);
        }

        /// <summary>
        /// 获取主机IP地址
        /// </summary>
        /// <param name="hostName">主机名称</param>
        /// <returns></returns>
        public static IPAddress GetHostIP(string hostName)
        {
            IPAddress[] hostAddresses = Dns.GetHostAddresses(hostName);
            IPAddress none = IPAddress.None;
            foreach (IPAddress address2 in hostAddresses)
            {
                if (address2.AddressFamily == AddressFamily.InterNetwork)
                {
                    return address2;
                }
            }
            return none;
        }

        #endregion

        #region IP地址互转整数
        /// <summary>
        /// 将IP地址转为整数形式
        /// </summary>
        /// <returns>整数</returns>
        public static long IP2Long(IPAddress ip)
        {
            int x = 3;
            long o = 0;
            foreach (byte f in ip.GetAddressBytes())
            {
                o += (long)f << 8 * x--;
            }
            return o;
        }
        /// <summary>
        /// 将整数转为IP地址
        /// </summary>
        /// <returns>IP地址</returns>
        public static IPAddress Long2IP(long l)
        {
            byte[] b = new byte[4];
            for (int i = 0; i < 4; i++)
            {
                b[3 - i] = (byte)(l >> 8 * i & 255);
            }
            return new IPAddress(b);
        }
        #endregion
    }

    #region IPLocator类
    /// <summary>
    /// IPLocator类
    /// </summary>
    public class IPLocator
    {
        /// <summary>
        /// 核心方法：IP搜索
        /// </summary>
        /// <param name="ips">ip地址</param>
        /// <returns></returns>
        public static IpLocation GetIpLocation(string ips)
        {
            string fn = PathHelper.Map(strUtil.Join(sys.Path.Root, "IPData/QQWry.Dat"));
            if (!File.Exists(fn))
            {
                throw new Exception("文件不存在!");
            }
            FileStream fs = new FileStream(fn, FileMode.Open, FileAccess.Read, FileShare.Read);
            BinaryReader fp = new BinaryReader(fs);
            //读文件头,获取首末记录偏移量
            int fo = fp.ReadInt32();
            int lo = fp.ReadInt32();
            //IP值
            uint ipv = IpStringToInt(ips);
            // 获取IP索引记录偏移值
            int rcOffset = getIndexOffset(fs, fp, fo, lo, ipv);
            fs.Seek(rcOffset, System.IO.SeekOrigin.Begin);

            IpLocation ipl;
            if (rcOffset >= 0)
            {
                fs.Seek(rcOffset, System.IO.SeekOrigin.Begin);
                //读取开头IP值
                ipl.IpStart = fp.ReadUInt32();
                //转到记录体
                fs.Seek(ReadInt24(fp), System.IO.SeekOrigin.Begin);
                //读取结尾IP值
                ipl.IpEnd = fp.ReadUInt32();
                ipl.Country = GetString(fs, fp);
                ipl.City = GetString(fs, fp);
            }
            else
            {
                //没找到
                ipl.IpStart = 0;
                ipl.IpEnd = 0;
                ipl.Country = "未知国家";
                ipl.City = "未知地址";
            }
            fp.Close();
            fs.Close();
            return ipl;
        }

        /// <summary>
        /// 函数功能: 采用“二分法”搜索索引区, 定位IP索引记录位置
        /// </summary>
        /// <param name="fs"></param>
        /// <param name="fp"></param>
        /// <param name="_fo"></param>
        /// <param name="_lo"></param>
        /// <param name="ipv"></param>
        /// <returns></returns>
        private static int getIndexOffset(FileStream fs, BinaryReader fp, int _fo, int _lo, uint ipv)
        {
            int fo = _fo, lo = _lo;
            int mo;    //中间偏移量
            uint mv;    //中间值
            uint fv, lv; //边界值
            uint llv;   //边界末末值
            fs.Seek(fo, System.IO.SeekOrigin.Begin);
            fv = fp.ReadUInt32();
            fs.Seek(lo, System.IO.SeekOrigin.Begin);
            lv = fp.ReadUInt32();
            //临时作它用,末记录体偏移量
            mo = ReadInt24(fp);
            fs.Seek(mo, System.IO.SeekOrigin.Begin);
            llv = fp.ReadUInt32();
            //边界检测处理
            if (ipv < fv)
                return -1;
            else if (ipv > llv)
                return -1;
            //使用"二分法"确定记录偏移量
            do
            {
                mo = fo + (lo - fo) / 7 / 2 * 7;
                fs.Seek(mo, System.IO.SeekOrigin.Begin);
                mv = fp.ReadUInt32();
                if (ipv >= mv)
                    fo = mo;
                else
                    lo = mo;
                if (lo - fo == 7)
                    mo = lo = fo;
            } while (fo != lo);
            return mo;
        }

        /// <summary>
        /// 字符串数值型判断
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static bool IsNumeric(string s)
        {
            if (s != null && System.Text.RegularExpressions.Regex.IsMatch(s, @"^-?\d+$"))
                return true;
            else
                return false;
        }

        /// <summary>
        /// IP字符串->长整型值
        /// </summary>
        /// <param name="IpString"></param>
        /// <returns></returns>
        public static uint IpStringToInt(string IpString)
        {
            uint Ipv = 0;
            string[] IpStringArray = IpString.Split('.');
            int i;
            uint Ipi;
            for (i = 0; i < 4 && i < IpStringArray.Length; i++)
            {
                if (IsNumeric(IpStringArray[i]))
                {
                    Ipi = (uint)Math.Abs(Convert.ToInt32(IpStringArray[i]));
                    if (Ipi > 255) Ipi = 255;
                    Ipv += Ipi << (3 - i) * 8;
                }
            }
            return Ipv;
        }

        /// <summary>
        /// 长整型值->IP字符串
        /// </summary>
        /// <param name="Ipv"></param>
        /// <returns></returns>
        public static string IntToIpString(uint Ipv)
        {
            string IpString = "";
            IpString += (Ipv >> 24) + "." + ((Ipv & 0x00FF0000) >> 16) + "." + ((Ipv & 0x0000FF00) >> 8) + "." + (Ipv & 0x000000FF);
            return IpString;
        }

        /// <summary>
        /// 读取字符串
        /// </summary>
        /// <param name="fp"></param>
        /// <returns></returns>
        private static string ReadString(BinaryReader fp)
        {
            byte[] TempByteArray = new byte[128];
            int i = 0;
            do
            {
                TempByteArray[i] = fp.ReadByte();
            } while (TempByteArray[i++] != '\0' && i < 128);
            return System.Text.Encoding.Default.GetString(TempByteArray).TrimEnd('\0');
        }

        /// <summary>
        /// 读取三字节的整数
        /// </summary>
        /// <param name="fp"></param>
        /// <returns></returns>
        private static int ReadInt24(BinaryReader fp)
        {
            if (fp == null) return -1;
            int ret = 0;
            ret |= (int)fp.ReadByte();
            ret |= (int)fp.ReadByte() << 8 & 0xFF00;
            ret |= (int)fp.ReadByte() << 16 & 0xFF0000;
            return ret;
        }

        /// <summary>
        /// 读取IP所在地字符串
        /// </summary>
        /// <param name="fs"></param>
        /// <param name="fp"></param>
        /// <returns></returns>
        private static string GetString(FileStream fs, BinaryReader fp)
        {
            byte Tag;
            int Offset;
            Tag = fp.ReadByte();
            if (Tag == 0x01)		// 重定向模式1: 城市信息随国家信息定向
            {
                Offset = ReadInt24(fp);
                fs.Seek(Offset, System.IO.SeekOrigin.Begin);
                return GetString(fs, fp);
            }
            else if (Tag == 0x02)	// 重定向模式2: 城市信息没有随国家信息定向
            {
                Offset = ReadInt24(fp);
                int TmpOffset = (int)fs.Position;
                fs.Seek(Offset, System.IO.SeekOrigin.Begin);
                string TmpString = GetString(fs, fp);
                fs.Seek(TmpOffset, System.IO.SeekOrigin.Begin);
                return TmpString;
            }
            else	// 无重定向: 最简单模式
            {
                fs.Seek(-1, System.IO.SeekOrigin.Current);
                return ReadString(fp);
            }
        }
    }

    /// <summary>
    /// IP查询结果结构
    /// </summary>
    public struct IpLocation
    {
        /// <summary>
        /// IP开始地址
        /// </summary>
        public uint IpStart;
        /// <summary>
        /// IP结束地址
        /// </summary>
        public uint IpEnd;
        /// <summary>
        /// 国家
        /// </summary>
        public string Country;
        /// <summary>
        /// 城市
        /// </summary>
        public string City;
    }

    #endregion
}
